const path = require('path'); // path 模块是什么，它是一个 Node.js 核心模块，用于操作文件路径。
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 自动打成dist目录的index.html

//const CleanWebpackPlugin = require('clean-webpack-plugin'); // 清理/dist目录
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 清理/dist目录,新版的写法

const webpack = require('webpack');

// 最小化css，less和sass
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'prod';

const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

const CopyPlugin = require('copy-webpack-plugin');

module.exports = {

  optimization: {
    minimizer: devMode ? [] : [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
    splitChunks: {
      // include all types of chunks
      // chunks: 'all'
      chunks: 'all',//表明将选择哪些块进行优化。当提供一个字符串，有效值为all，async和initial。提供all可能特别强大，因为它意味着即使在异步和非异步块之间也可以共享块。
      minSize: 30000,
      maxSize: 0, //生成块的最小大小（以字节为单位）。
      minChunks: 1,//拆分前必须共享模块的最小块数。
      maxAsyncRequests: 5,//按需加载时最大并行请求数。
      maxInitialRequests: 3,//入口点的最大并行请求数。
      automaticNameDelimiter: '~',//默认情况下，webpack将使用块的来源和名称生成名称（例如vendors~main.js）。此选项使您可以指定用于生成名称的定界符
      name: true,//拆分块的名称。提供true将基于块和缓存组密钥自动生成一个名称。提供字符串或函数将允许您使用自定义名称。如果名称与入口点名称匹配，则入口点将被删除。
      //name (module, chunks, cacheGroupKey) { // 第二种以函数，第三种返回string
      // generate a chunk name...
      //return; //...
      //},
      cacheGroups: {
        // 将所有CSS提取到一个文件中
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
        },
        vendors: {
          //test: /[\\/]node_modules[\\/]/, //控制此缓存组选择哪些模块。省略它会选择所有模块。它可以匹配绝对模块资源路径或块名称。匹配块名称时，将选择该块中的所有模块。
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, // 只导出核心模块
          name: 'vendor',
          chunks: 'all',
          priority: -10 //一个模块可以属于多个缓存组。优化将首选具有较高的缓存组priority。默认组的优先级为负，以允许自定义组获得更高的优先级（默认值适用0于自定义组）。
        },
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true //如果当前块包含已从主捆绑包中拆分出的模块，则将重用该模块，而不是生成新的模块。这可能会影响块的结果文件名。
        }
      }
    }
  },

  // 入口：不配，默认值是 ./src/index.js
  entry: './src/index.js',
  //entry: './src/indexReact.js', //react测试
  //entry: './src/index.tsx', // react TypeScript测试
  //entry: './src/react-study/class/index.js', // 学习react测试
  //entry: './src/react-study/base/base2.js',
  //entry: {
    //app: './src/index.js',
    //print: './src/print.js'
  //},
  // 输出: 告诉 webpack 在哪里输出它所创建的 bundle，以及如何命名这些文件。
  output: {
    // 主要输出文件的默认值是 ./dist/main.js，其他生成文件默认放置在 ./dist 文件夹中。
    path: path.resolve(__dirname, 'dist'), //webpack.config.js的路径是__dirname，要找到dist目录即。root的默认值就是__dirname
    // 通过 output.filename 和 output.path 属性，来告诉 webpack bundle 的名称，以及我们想要 bundle 生成(emit)到哪里。
    //filename: 'my-first-webpack.bundle.js' // filename: 'bundle.js',
    //filename: 'main.js',
    filename: '[name].bundle.js', // 根据entry指定的入口动态输出多文件
    chunkFilename: '[name].[chunkhash].chunk.js',//决定非入口 chunk 的名称
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'], // there's a dot missing
  },
  // 如果使用TypeScript时向加快编译，可以直接在文件使用直接引入react基础组件，跳过编译（因为Ts使用@types/react组件）
  //externals: {
    //"react": "React",
    //"react-dom": "ReactDOM"
  //},
  module: { // 默认webpack只认识js和json。对于其他文件不能识别，所以如果需要一起打包其他文件资源，都要配置这个
    rules: [
      {
        test: /\.ts(x?)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "ts-loader"
          }
        ]
      },
      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      {
        enforce: "pre",
        test: /\.js$/,
        loader: "source-map-loader"
      },
      // 在 webpack 的配置中 loader 有两个属性
      // test 属性，用于标识出应该被对应的 loader 进行转换的某个或某些文件。
      // use 属性，表示进行转换时，应该使用哪个 loader。
      // webpack 编译器，当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时，在你对它打包之前，先 使用 raw-loader 转换一下。
      //{ test: /\.txt$/, use: 'raw-loader' }
      // 所有以 .css 结尾的文件，都将被提供给 style-loader 和 css-loader
      { test: /\.css$/, use: [
        devMode ? 'style-loader' :
        {
          loader: MiniCssExtractPlugin.loader,
          options: {
            hot: process.env.NODE_ENV === 'dev',
          },
        },
        'css-loader','postcss-loader'] },
      //{ test: /\.css$/, use: ['style-loader','css-loader'] },
      // less
      {
        test: /\.less$/,
        use: [
        devMode ? 'style-loader' :
        {
          loader: MiniCssExtractPlugin.loader,
          options: {
            hot: process.env.NODE_ENV === 'dev',
          },
        }, 
        {
          loader: 'css-loader' // translates CSS into CommonJS
        }, 
        {
          loader: 'postcss-loader' // 浏览器前缀插件loader,postcss-loader必须配置在css-loader之前 。
        },
        {
          loader: 'less-loader', // compiles Less to CSS
          options: {
            modifyVars: {
              'primary-color': '#FF0000',
              'link-color': '#1DA57A',
              'border-radius-base': '2px',
            },
            javascriptEnabled: true,
          }
        }]
      },
      {
        test: /\.(sa|sc)ss$/,
        use: [
          devMode ? 'style-loader' :
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hot: process.env.NODE_ENV === 'dev',
            },
          },
          'css-loader',
          'postcss-loader', // 浏览器前缀插件loader,postcss-loader必须配置在css-loader之前 。
          'sass-loader',
        ],
      },
      // 字体加载
      { test: /\.(woff|woff2|eot|ttf|otf)$/, use: ['file-loader'] },
      // csv文件和xml文件加载
      { test: /\.(csv|tsv)$/, use: ['csv-loader'] },
      { test: /\.xml$/, use: ['xml-loader'] },
      // 图片加载
      { test: /\.(png|svg|jpg|gif)$/, use: ['file-loader'] },
      // 增加babel-loader的加载js的转码规则
      {
        //test: /\.js$/,
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/, //排除掉node_module目录
        use: {
          loader: 'babel-loader',
          //options: {
          //presets: ['@babel/preset-env'], //转码规则
          //plugins: ['@babel/plugin-transform-runtime'] // 加快编译的速度
          //}
        }
      }
    ]
  },

  // 插件: loader 用于转换某些类型的模块，而插件则可以用于执行范围更广的任务。包括：打包优化，资源管理，注入环境变量。
  plugins: [ // 插件列表https://webpack.docschina.org/plugins
    //new webpack.DllReferencePlugin({ // 旧的打公共包 先执行 npm run dll ，会执行webpack.dll.config.js配置
    //manifest: require("./vendor/manifest.json"),
    //context: __dirname
    //}),
    new MiniCssExtractPlugin({
      // 这里的配置和webpackOptions.output中的配置相似
      // 即可以通过在名字前加路径，来决定打包后的文件存在的路径
      filename: devMode ? '[name].css' : '[name].[contenthash].css',
      chunkFilename: devMode ? '[id].css' : '[id].[contenthash].css',
    }),
    // html-webpack-plugin 为应用程序生成 HTML 一个文件，并自动注入所有生成的 bundle
    //new HtmlWebpackPlugin({template: './src/index.html'})
    //new HtmlWebpackPlugin({title: '管理输出',template:'./index.html'}), //dist目录生成一个标题为管理输出的index.html
    //new HtmlWebpackPlugin({template: './public/index.html'}), //dist目录生成一个标题为管理输出的index.html
    //new HtmlWebpackPlugin({template: './index.html'}), //dist目录生成一个标题为管理输出的index.html
    new HtmlWebpackPlugin({title: '学习react',template: './public/index.html'}), // 学习react测试
    new CleanWebpackPlugin(), // 清理/dist 目录。每次打包生成新的 // 自定义删除参考https://www.jianshu.com/p/dd67a3b64b62
    // 最新版本已经抛弃：建议使用SplitChunksPlugin
    //new webpack.optimize.CommonsChunkPlugin({name: 'common'}), //// common 指定公共 bundle 的名称。(有公共的会提取成common.bundle.js) 防止重复
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true),
      VERSION: JSON.stringify('5fa3b9'),
      BROWSER_SUPPORTS_HTML5: true,
      TWO: '1+1',
      'typeof window': JSON.stringify('object'),
      //'process.env.PUBLIC_URL': JSON.stringify('./public'),
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      '$NODE_ENV': JSON.stringify(process.env.NODE_ENV), // 解决不能在reactjs中直接获取process.env.NODE_ENV的解决方案
      'SERVICE_URL': JSON.stringify('http://dev.example.com')
    }),
    // 忽略资源文件和这表示“任何'./locale'以结尾结尾的目录中匹配的require语句'moment'都将被忽略。
    new webpack.IgnorePlugin({ //忽略文件参考https://webpack.docschina.org/plugins/ignore-plugin/#using-filter-functions
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/
    }),
    // npm i copy-webpack-plugin -D
    new CopyPlugin([
      {
        from: path.join(__dirname, 'public'),
        to: path.join(__dirname, '.', 'dist', 'public'),
        //to: path.join(__dirname, '.', 'dist'),
        toType: 'dir',
        ignore: [
          '.DS_Store',
          '.gitkeep',
          'template.ejs',
          'index.html'
        ]
      }
    ])
  ]

};